from fastapi import APIRouter, Depends, HTTPException, Security
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse

from myProfile.db_config.odmantic_config import create_db_engine
from myProfile.models.data.myProfile_models import Signup, Login
from myProfile.repository.signup import SignupRepository
from myProfile.repository.login import LoginRepository
from myProfile.models.request.myProfile_request import LoginReq

from fastapi.security import OAuth2PasswordRequestForm
from myProfile.security.secure import authenticate, get_password_hash
from myProfile.security.secure import get_current_valid_user, \
    create_access_token
from myProfile.security.secure import ACCESS_TOKEN_EXPIRE_MINUTES

from datetime import date, timedelta, datetime
from json import dumps, loads

router = APIRouter()


def json_serial(obj):
    if isinstance(obj, (datetime, date)):
        return obj.strftime('%Y-%m-%dT%H:%M:%S.%f')
    raise TypeError("The type %s not serializable." % type(obj))


@router.get("/approve/signup")
async def signup_approve(username: str, current_user:
                         Login = Security(get_current_valid_user,
                                          scopes=["admin_write"]),
                         engine=Depends(create_db_engine)):
    signuprepo = SignupRepository(engine)
    result: Signup = await signuprepo.get_signup_username(username)

    if result is None:
        return JSONResponse(
            content={'message': 'username is not valid'}, status_code=401)
    else:
        passphrase = get_password_hash(result.password)
        loginrepo = LoginRepository(engine)
        user: Login = await loginrepo.get_login_username(result.username)
        if user is not None:
            return JSONResponse(
                content={"message":
                         f"username {result.username} already exists."},
                status_code=403)

        login = {'id': result.id, 'username': result.username,
                 'password': result.password, 'passphrase': passphrase,
                 'approved_date': datetime.today()}
        success = await loginrepo.insert_login(login)
        if success is False:
            return JSONResponse(
                content={'message': 'create login problem encountered'},
                status_code=500)
        else:
            return login


@router.post("/login/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends(),
                engine=Depends(create_db_engine)):
    username = form_data.username
    password = form_data.password
    loginrepo = LoginRepository(engine)
    account = await loginrepo.get_login_username(username)
    scopes = account.permission_sets
    if authenticate(username, password, account) and account is not None:
        access_token = create_access_token(
            data={"sub": username, "scopes": scopes},
            expires_delta=timedelta(
                minutes=ACCESS_TOKEN_EXPIRE_MINUTES
            )
        )
        return {"access_token": access_token, "token_type": "bearer"}
    else:
        raise HTTPException(
            status_code=400, detail="Incorrect username or password")


@router.patch("/login/update/permission")
async def update_login(id: int, req: LoginReq,
                       current_user: Login = Security(
                           get_current_valid_user, scopes=["admin_write"]),
                       engine=Depends(create_db_engine)):
    login_dict = req.model_dump(exclude_unset=True)
    login_json = dumps(login_dict, default=json_serial)
    repo: LoginRepository = LoginRepository(engine)
    result = await repo.update_login(id, loads(login_json))
    if result is True:
        return req
    else:
        return JSONResponse(
            content={"message": "update login unsuccessful"},
            status_code=500)


@router.delete("/login/delete/{id}")
async def delete_login(id: int, current_user:
                       Login = Security(get_current_valid_user,
                                        scopes=["admin_write"]),
                       engine=Depends(create_db_engine)):
    loginrepo = LoginRepository(engine)
    result = await loginrepo.delete_login(id)
    if result is True:
        return JSONResponse(
            content={'message': 'login deleted successfully'}, status_code=201)
    else:
        raise HTTPException(
            status_code=400, detail="Incorrect username or password")


@router.get("/login/get/all")
async def get_all_login(current_user:
                        Login = Security(get_current_valid_user,
                                         scopes=["admin_read"]),
                        engine=Depends(create_db_engine)):
    loginrepo = LoginRepository(engine)
    users = await loginrepo.get_all_login()
    return jsonable_encoder(users)


@router.get("/login/get/username")
async def get_login_username(username: str,
                             current_user: Login =
                             Security(get_current_valid_user,
                                      scopes=["admin_read"]),
                             engine=Depends(create_db_engine)):
    repo: LoginRepository = LoginRepository(engine)
    return jsonable_encoder(await repo.get_login_username(username))


@router.get("/login/get/{id}")
async def get_login(id: int,
                    current_user: Login = Security(
                        get_current_valid_user, scopes=["admin_read"]),
                    engine=Depends(create_db_engine)):
    repo: LoginRepository = LoginRepository(engine)
    return jsonable_encoder(await repo.get_login(id))
